// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package dfa.axis.v1_19.matchtables;

import com.google.api.ads.dfa.axis.v1_19.ApiException;
import com.google.api.ads.dfa.axis.v1_19.Base;
import com.google.api.ads.dfa.axis.v1_19.PageableSearchCriteriaBase;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;

/**
 * Encapsulates all page-able SOAP request and retry logic
 * 
 * @author Moshe Kahan
 */
class SoapPageableLookup<Record extends Base, Criteria extends PageableSearchCriteriaBase>
  extends AbstractSoapLookup<Record> {

  private static Random randomGenerator = new Random();

  private SoapPageableLookupCallback<Record, Criteria> soapLookupCallback;

  /**
   * Construct SoapPageableLookup without any input entity IDs.
   */
  SoapPageableLookup(SoapPageableLookupCallback<Record, Criteria> soapLookupCallback) {
    super();
    this.soapLookupCallback = soapLookupCallback;
  }

  /**
   * Construct SoapPageableLookup with collection of input entity ID Longs.
   */
  SoapPageableLookup(Collection<Long> entityIdCollection,
      SoapPageableLookupCallback<Record, Criteria> soapLookupCallback) {
    super(entityIdCollection);
    this.soapLookupCallback = soapLookupCallback;
  }

  /**
   * Perform a paginated SOAP lookup on this sub-set of IDs.
   *
   * @param ids set of IDs to retrieve entities for.
   */
  @Override
  void executeRequest(long[] ids) throws DfaSoapException {
    // Retrieve 100 records at a time.
    Criteria criteria = soapLookupCallback.getCriteria();
    criteria.setPageSize(100);
    // When paging, start counting page numbers from 1 rather than 0.
    criteria.setPageNumber(1);
    if (ids != null) {
      criteria.setIds(ids);
    }
    int recordPages = 0;
    clearRecordArray();
    do {
      List<Record> subRecordArray = new ArrayList<Record>();
      boolean success = false;
      int attempts = 0;
      do {
        try {
          ++attempts;
          recordPages = soapLookupCallback.getRecords(criteria, subRecordArray);
          success = true;
        } catch (ApiException e) {
          System.err.println("SOAP ERROR! Attempt #" + attempts + " with exception: " + e);
          e.printStackTrace(System.err);
          if (attempts > 2) {
            System.err.println("Received APIException from DFA SOAP API event after 3 attempts."
                + " Skipping...");
            break;
          }
        } catch (RemoteException e) {
          System.err.println("SOAP ERROR! Attempt #" + attempts + " with exception: " + e);
          e.printStackTrace(System.err);
          if (attempts > 2) {
            throw new DfaSoapException(
                "Received RemoteException from DFA SOAP API event after 3 attempts", e);
          }
        }
        try {
          // Sleep for 1 to 5 seconds.
          Thread.sleep(randomGenerator.nextInt(5) * 1000 + 1000);
        } catch (InterruptedException e) {
          e.printStackTrace(System.err);
        }
      } while (!success);
      addRecords(subRecordArray);
      criteria.setPageNumber(criteria.getPageNumber() + 1);
    } while (criteria.getPageNumber() <= recordPages);
    resetRecordArrayIterator();
  }
}
